#ifndef PCPS_EXPECTATION_VALUE_HEADER
#define PCPS_EXPECTATION_VALUE_HEADER

#include <src/pcps.h>
#include <src/correlator.h>
#include <src/operator.h>
#include <src/cluster.h>

namespace pcps {

  //-------------------------------------------------------------------------------
  // pcps::expectation_value -- Computes the projective cps approximation to the
  //                            operator's expectation value.
  //                            The operator is stored in op.
  //                            The expectation value is computed up to order
  //                            perturbation_level in the perturbative correlators.
  //
  //                            This particular function constructs the cluster
  //                            around the operator and determines the relavant
  //                            correlators.
  //                            The expectation_value_master function is then
  //                            called, which deals with computing the result in
  //                            parallel if farm parallel mode has been specified.
  //                            Eventually, after a number of other function calls
  //                            to determine the operator, particle, and reference
  //                            types, the expectation_value_detail function is
  //                            called, which computes the expectation value.
  //-------------------------------------------------------------------------------

  template<class SCALAR>
  void expectation_value(const std::vector<int> & slaves,
                         int & slaves_used,
                         int & jobs_in_progress,
                         SCALAR * const output_array,
                         const int array_pos,
                         const SCALAR coeff,
                         const pcps::Input & userinp,
                         const pcps::Wavefunction<SCALAR> & wfn,
                         const typename pcps::OpBase<SCALAR>::const_ptr & op,
                         const int perturbation_level,
                         const pcps::Cluster & fixed_bra_occs,
                         const pcps::Cluster & fixed_ket_occs,
                         const typename pcps::Correlator<SCALAR>::Set & excluded);

  //-------------------------------------------------------------------------------------
  // pcps::expectation_value_detail -- computes the 'projective' expectation value of an
  //                                   operator by looping over the configurations of its
  //                                   cluster.
  //                                   The function is templated on the scalar type
  //                                   (SCALAR), the operator type (OP), the particle
  //                                   type (PART), and the reference type (REF).
  //-------------------------------------------------------------------------------------

  template <class SCALAR, class OP, class PART, class REF>
  SCALAR expectation_value_detail(const pcps::Input & userinp,
                                  const pcps::Wavefunction<SCALAR> & wfn,
                                  const OP & op_input,
                                  const int perturbation_level,
                                  pcps::Cluster & local_cluster,
                                  const typename pcps::Correlator<SCALAR>::Set & cluster_corrs,
                                  const typename pcps::Correlator<SCALAR>::Set & pert_cluster_corrs);

  //-------------------------------------------------------------------------------------
  // pcps::expectation_value_choose_ref -- Determines which wavefunction reference is
  //                                       being used and calls the appropriate template.
  //
  //                                       This function is templated on the scalar type
  //                                       (SCALAR), the operator type (OP), and the
  //                                       particle type (PART).
  //-------------------------------------------------------------------------------------

  template <class SCALAR, class OP, class PART>
  SCALAR expectation_value_choose_ref(const pcps::Input & userinp,
                                      const pcps::Wavefunction<SCALAR> & wfn,
                                      const OP & op,
                                      const int perturbation_level,
                                      pcps::Cluster & local_cluster,
                                      const typename pcps::Correlator<SCALAR>::Set & cluster_corrs,
                                      const typename pcps::Correlator<SCALAR>::Set & pert_cluster_corrs);

  //-------------------------------------------------------------------------------------
  // pcps::expectation_value_choose_part -- Determines which particle type is being used
  //                                        and calls the appropriate template.
  //
  //                                        This function is templated on the scalar type
  //                                        (SCALAR) and the operator type (OP).
  //-------------------------------------------------------------------------------------

  template <class SCALAR, class OP>
  SCALAR expectation_value_choose_part(const pcps::Input & userinp,
                                       const pcps::Wavefunction<SCALAR> & wfn,
                                       const OP & op,
                                       const int perturbation_level,
                                       pcps::Cluster & local_cluster,
                                       const typename pcps::Correlator<SCALAR>::Set & cluster_corrs,
                                       const typename pcps::Correlator<SCALAR>::Set & pert_cluster_corrs);

  //-------------------------------------------------------------------------------------
  // pcps::expectation_value_choose_op -- Determines which operator type is being used
  //                                      and calls the appropriate template.
  //
  //                                      This function is templated on the scalar
  //                                      type (SCALAR).
  //-------------------------------------------------------------------------------------

  template <class SCALAR>
  SCALAR expectation_value_choose_op(const pcps::Input & userinp,
                                     const pcps::Wavefunction<SCALAR> & wfn,
                                     const typename pcps::OpBase<SCALAR>::const_ptr & op,
                                     const int perturbation_level,
                                     pcps::Cluster & local_cluster,
                                     const typename pcps::Correlator<SCALAR>::Set & cluster_corrs,
                                     const typename pcps::Correlator<SCALAR>::Set & pert_cluster_corrs);

  //-------------------------------------------------------------------------------
  // pcps::expectation_value_master -- assigns slaves to compute an expectation
  //                                   value, or computes it directly if there are
  //                                   no slaves
  //-------------------------------------------------------------------------------

  template <class SCALAR>
  void expectation_value_master(const std::vector<int> & slaves,
                                int & slaves_used,
                                int & jobs_in_progress,
                                SCALAR * const output_array,
                                const int array_pos,
                                const SCALAR coeff,
                                const pcps::Input & userinp,
                                const pcps::Wavefunction<SCALAR> & wfn,
                                const typename pcps::OpBase<SCALAR>::const_ptr & op,
                                const int perturbation_level,
                                pcps::Cluster & local_cluster,
                                const std::vector<int> & corr_pos,
                                const std::vector<int> & pert_corr_pos);

  //-------------------------------------------------------------------------------
  // pcps::expectation_value_slave -- places a slave process in a state in which it
  //                                  receives instructions to compute expectation
  //                                  values from the master process
  //-------------------------------------------------------------------------------

  template <class SCALAR>
  void expectation_value_slave(const pcps::Input & userinp,
                               const pcps::Wavefunction<SCALAR> & wfn);

  //-------------------------------------------------------------------------------
  // pcps::recv_expectation_value -- receives an expectation value from a slave
  //                                 process and adds it to the appropriate
  //                                 element of the ouput array
  //-------------------------------------------------------------------------------

  template <class SCALAR>
  void recv_expectation_value(int & slave,
                              int & jobs_in_progress,
                              SCALAR * const output_array);

  //-------------------------------------------------------------------------------
  // pcps::collect_expectation_values -- collects the expectation values from
  //                                     any slave processes that are still working
  //                                     and tells all the slaves that there is no
  //                                     more work to do
  //-------------------------------------------------------------------------------

  template <class SCALAR>
  void collect_expectation_values(const std::vector<int> & slaves,
                                  int & jobs_in_progress,
                                  SCALAR * const output_array);

}

#endif
